library(shiny)
library(tidyverse)
library(shinythemes)
Warning: package ‘shinythemes’ was built under R version 4.1.2
library(stringi)
library(RColorBrewer)
seabirds_cleaned_data <- read_csv("clean_data/seabirds_cleaned_data.csv")
Rows: 49020 Columns: 52
-- Column specification ---------------------------------------------------
Delimiter: ","
chr  (19): common_name, scientific_name, species_abbreviation, age, plp...
dbl  (28): record_x, record_id, wanplum, total_sighting, num_feeding, n...
lgl   (3): sex, air_temp, salinity
dttm  (2): date, time

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.

birds_21 <- seabirds_cleaned_data %>%
  mutate(bird_type = case_when(
  str_detect(common_name, 
             regex("shearwater", 
                   ignore_case = TRUE)) ~ "Shearwater",
  str_detect(common_name, 
             regex("albatross", 
                   ignore_case = TRUE)) ~ "Albatross",
  str_detect(common_name, 
             regex("mollymawk", 
                   ignore_case = TRUE)) ~ "Mollymawk",
  str_detect(common_name, 
             regex("petrel", 
                   ignore_case = TRUE)) ~ "Petrel",
  str_detect(common_name, 
             regex("prion", 
                   ignore_case = TRUE)) ~ "Prion",
  str_detect(common_name, 
             regex("skua", 
                   ignore_case = TRUE)) ~ "Skua",
  str_detect(common_name, 
             regex("penguin", 
                   ignore_case = TRUE)) ~ "Penguin",
  str_detect(common_name, 
             regex("tropicbird", 
                   ignore_case = TRUE)) ~ "Tropicbird",
  str_detect(common_name, 
             regex("noddy", 
                   ignore_case = TRUE)) ~ "Noddy",
  str_detect(common_name, 
             regex("tern", 
                   ignore_case = TRUE)) ~ "Tern",
  str_detect(common_name, 
             regex("gull", 
                   ignore_case = TRUE)) ~ "Gull",
  str_detect(common_name, 
             regex("booby", 
                   ignore_case = TRUE)) ~ "Booby",
  str_detect(common_name, 
             regex("frigatebird", 
                   ignore_case = TRUE)) ~ "Frigatebird",
  str_detect(common_name, 
             regex("shag", 
                   ignore_case = TRUE)) ~ "Shag",
  str_detect(common_name, 
             regex("sheathbill", 
                   ignore_case = TRUE)) ~ "Sheathbill",
  str_detect(common_name, 
             regex("fulmar", 
                   ignore_case = TRUE)) ~ "Fulmar",
  str_detect(common_name, 
             regex("gannet", 
                   ignore_case = TRUE)) ~ "Gannet",
  str_detect(common_name, 
             regex("cormorant", 
                   ignore_case = TRUE)) ~ "Cormorant",
  str_detect(common_name, 
             regex("procellaria", 
                   ignore_case = TRUE)) ~ "Procellaria",
    TRUE ~ common_name))

birds_21
birds_21 %>% 
  arrange(date)
# https://r-charts.com/color-palette-generator/

# https://www.statology.org/color-by-factor-ggplot2/

birds_pal <- c("#50e2ea", "#4edae5", "#4bd2df", "#49cada", "#47c2d4", 
               "#45bbcf", "#42b3c9", "#40abc4", "#3ea3be", "#3b9bb9",
               "#3993b3", "#378bae", "#3483a8", "#327ba3", "#30739d", 
               "#2e6c98", "#2b6492", "#295c8d", "#275487", "#244c82", "#22447c")

names(birds_pal) <- levels(birds_21$bird_type)

custom_colors <- scale_colour_manual(values = birds_pal)
  

birds <- c("Tropicbird" = "#50e2ea", "Tern" = "#4edae5", "Skua" = "#4bd2df", 
           "Sheathbill" = "#49cada", "Shearwater" = "#47c2d4", 
           "Shag" = "#45bbcf", "Seabird" = "#42b3c9", "Procellaria" = "#40abc4",
           "Prion" = "#3ea3be", "Petrel" = "#3b9bb9", "Penguin" = "#3993b3", 
           "Noddy" = "#378bae", "Mollymawk" = "#3483a8", "Jaeger" = "#327ba3", 
           "Gull" = "#30739d", "Gannet" = "#2e6c98", "Fulmar" = "#2b6492", 
           "Frigatebird" = "#295c8d", "Cormorant" = "#275487", 
           "Booby" = "#244c82", "Albatross" = "#22447c")

Jaeger Seabird

birds_21 %>% 
  filter(!is.na(bird_type)) %>% 
  count(bird_type)  
NA

Bird Seen tab

not updated birds so have 3 extra with no colour asigned, shiny up to date

sighting <-  birds_21 %>% 
            filter(!is.na(bird_type)) %>% 
            group_by(bird_type) %>% 
            summarise(count = sum(total_sighting, na.rm = TRUE)) %>%
            mutate(sighting_id = row_number())

sighting %>% 
    ggplot() +
    aes(y = bird_type, 
        x = count, fill = bird_type) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous(breaks = c(1, 5, 10, 1000, 6000, 1400000),
                       limits = c(1,1400000), 
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen \n Log10 scale") +
    scale_fill_manual(values = birds)


# log10() as 1 or more birds are less than 10 and don't show on normal graph

1,394,468

feeding <-  birds_21 %>% 
              group_by(bird_type) %>% 
              filter(str_detect(feeding, "YES")) %>% 
              summarise(count = n()) %>% 
              mutate(feeding_id = row_number())

feeding %>% 
    ggplot() +
    aes(y = bird_type, 
        x = count, fill = bird_type) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous(breaks = c(1, 5, 10, 100, 300, 800),
                       limits = c(1,800), 
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Feeding \n Log10 scale") +
    scale_fill_manual(values = birds)

# log10() as 1 or more birds are less than 10 and don't show on normal graph
on_ship <-  birds_21 %>% 
              group_by(bird_type) %>% 
              filter(str_detect(on_ship, "YES")) %>% 
              summarise(count = n()) %>% 
              mutate(on_ship_id = row_number())

on_ship %>% 
    ggplot() +
    aes(y = bird_type, 
        x = count, fill = bird_type) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous(breaks = c(1, 2, 3, 5, 7, 10, 60),
                       limits = c(1,60), 
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen On Ship") +
    scale_fill_manual(values = birds)

in_hand <-  birds_21 %>% 
              group_by(bird_type) %>% 
              filter(str_detect(in_hand, "YES")) %>% 
              summarise(count = n()) %>% 
              mutate(in_hand_id = row_number()) 

in_hand %>% 
    ggplot() +
    aes(y = bird_type, 
        x = count, fill = bird_type) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen In Hand") +
    scale_fill_manual(values = birds)

# https://stackoverflow.com/questions/14255533/pretty-ticks-for-log-normal-scale-using-ggplot2-dynamic-not-manual
# https://stackoverflow.com/questions/43974892/dynamic-limits-and-breaks-in-scale-y-continuous


fly_by <-  birds_21 %>% 
              group_by(bird_type) %>% 
              filter(str_detect(fly_by, "YES")) %>% 
              summarise(count = n()) %>% 
              mutate(fly_by_id = row_number())


# base_breaks <- function(n = 10){
#     function(x) {
#         axisTicks(log10(range(fly_by$count, na.rm = TRUE)), 
#                   log = if_else(max(fly_by$count) > 1000, TRUE, FALSE), n = n)
#     }
# }

# asd <- if_else(max(fly_by$count) < 1000, c(limits=c(0,max(fly_by$count)), 
#                                            breaks  = seq(0,max(fly_by$count),
#                                             by = round(max(fly_by$count)/5))), 
#                c(limits=c(0,max(fly_by$count)), 
#                  breaks  = seq(0,max(fly_by$count), 
#                   by = round(max(fly_by$count)/5)),
#                  trans = "log10")
#                )

fly_by %>% 
    ggplot() +
    aes(y = bird_type, 
        x = count, fill = bird_type) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous(limits=c(0,max(fly_by$count)), 
                       breaks  = c(seq(0,max(fly_by$count),
                        by = (max(fly_by$count)/5))), trans = "log10"
                       #validate(max(fly_by$count) < 1000, trans = "log10")
                       ) +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Flying BY\n Log10 scale") +
    scale_fill_manual(values = birds)
Error in seq.default(a, b, length.out = n + 1) : 
  'from' must be a finite number

breaks = c(1, 5, 10, 1000, 6000), limits = c(1,6000), trans = “log10”

Tab 2 Variants tab

variants <- birds_21 %>% 
              filter(bird_type == "Albatross") %>% 
              group_by(common_name) %>% 
              summarise(count = n())
  
variants %>% 
    ggplot() +
    aes(y = common_name, 
        x = count, fill = common_name) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous(
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Flying BY\n Log10 scale") +
    scale_fill_manual(values = birds_pal)            

variants <- birds_21 %>% 
              filter(bird_type == "Booby") %>% 
              group_by(common_name) %>% 
              summarise(count = n())
  
variants %>% 
    ggplot() +
    aes(y = common_name, 
        x = count, fill = common_name) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous( 
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Flying BY\n Log10 scale") +
    scale_fill_manual(values = birds_pal)  

variants <- birds_21 %>% 
              filter(bird_type == "Cormorant") %>% 
              group_by(common_name) %>% 
              summarise(count = n())
  
variants %>% 
    ggplot() +
    aes(y = common_name, 
        x = count, fill = common_name) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous( 
                       trans = "log10") +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Flying BY\n Log10 scale") +
    scale_fill_manual(values = birds_pal) 

variants <- birds_21 %>% 
              filter(bird_type == "Tropicbird") %>% 
              group_by(common_name) %>% 
              summarise(count = n())
  
variants %>% 
    ggplot() +
    aes(y = common_name, 
        x = count, fill = common_name) +
    geom_col(colour = "black") +
    theme(legend.position = "none") +
    scale_x_continuous() +
    labs(y = "\n Bird Names",
         x = "Number of Birds Seen Flying BY\n Log10 scale") +
    scale_fill_manual(values = birds_pal) 

Tab 3 Sightings tab tab

library(shiny)
library(tidyverse)
library(shinythemes)


#seabirds_cleaned_data <- read_csv("data/seabirds_cleaned_data.csv")
# birds_9 <- seabirds_cleaned_data %>% 
#   group_by(common_name) %>% 
#   mutate(common_name = if_else(str_detect(common_name, 
#                                           "(?i)shearwater"),"Shearwater", 
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)albatross"), "Albatross",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)mollymawk"), "Mollymawk",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)petrel"), "Petrel",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)prion"), "Prion",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)skua"), "Skua",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)penguin"), "Penguin",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)Red-tailed tropicbird"), 
#                                "Red-tailed tropicbird",
#                                common_name),
#          common_name = if_else(str_detect(common_name, 
#                                           "(?i)Brown noddy"), "Brown noddy",
#                                common_name)
#   ) %>% 
#   filter(common_name %in% c("Shearwater", "Albatross", 
#                             "Mollymawk", "Petrel", 
#                             "Prion", "Skua", 
#                             "Penguin", "Brown noddy", 
#                             "Red-tailed tropicbird"))
# pal <- c("Shearwater" = "grey", "Albatross" = "blue", 
#          "Mollymawk" = "yellow", "Petrel" = "green", 
#          "Prion" = "pink", "Skua" = "purple", 
#          "Penguin" = "orange", "Brown noddy" = "brown", 
#          "Red-tailed tropicbird" = "red")
# names(birds_9)
# head(birds_9)

# birds_9 %>% 
#   group_by(common_name) %>% 
#   mutate(feeding = if_else(feeding %in% "YES", 1, 0),
#          on_ship = if_else(on_ship %in% "YES", 1, 0),
#          in_hand = if_else(in_hand %in% "YES", 1, 0),
#          fly_by = if_else(fly_by %in% "YES", 1, 0)) %>% 
#   summarise(sighting_count = sum(total_sighting, na.rm = TRUE),
#             feeding_count = sum(feeding, na.rm = TRUE),
#             on_ship_count = sum(on_ship, na.rm = TRUE),
#             in_hand_count = sum(in_hand, na.rm = TRUE),
#             fly_by_count = sum(fly_by, na.rm = TRUE)) 
library(leaflet)
getColor <- function(sight_map) {
  sapply(sight_map$bird_type, function(sight_map) {
  case_when(sight_map$bird_type == "Tropicbird" ~ "#50e2ea",
            sight_map$bird_type == "Tern" ~ "#4edae5",
            sight_map$bird_type == "Skua" ~ "#4bd2df",
            sight_map$bird_type == "Sheathbill" ~ "#49cada",
            sight_map$bird_type == "Shearwater" ~ "#47c2d4",
            sight_map$bird_type == "Shag" ~ "#45bbcf",
            sight_map$bird_type == "Seabird" ~ "#42b3c9",
            sight_map$bird_type == "Procellaria" ~ "#40abc4",
            sight_map$bird_type == "Prion" ~ "#3ea3be", 
            sight_map$bird_type == "Petrel" ~ "#3b9bb9", 
            sight_map$bird_type == "Penguin" ~ "#3993b3",
            sight_map$bird_type == "Noddy" ~ "#378bae", 
            sight_map$bird_type == "Mollymawk" ~ "#3483a8", 
            sight_map$bird_type == "Jaeger" ~ "#327ba3", 
            sight_map$bird_type == "Gull" ~ "#30739d", 
            sight_map$bird_type == "Gannet" ~ "#2e6c98", 
            sight_map$bird_type == "Fulmar" ~ "#2b6492",
            sight_map$bird_type == "Frigatebird" ~ "#295c8d",
            sight_map$bird_type == "Cormorant" ~ "#275487",
            sight_map$bird_type == "Booby" ~ "#244c82", 
            sight_map$bird_type == "Albatross" ~ "#22447c",
            TRUE ~ sight_map$bird_type
    
  ) })
}


icons <- awesomeIcons(
  icon = 'ios-close',
  iconColor = 'black',
  library = 'ion',
  markerColor = list("Tropicbird" = "#50e2ea", "Tern" = "#4edae5", "Skua" = "#4bd2df", 
           "Sheathbill" = "#49cada", "Shearwater" = "#47c2d4", 
           "Shag" = "#45bbcf", "Seabird" = "#42b3c9", "Procellaria" = "#40abc4",
           "Prion" = "#3ea3be", "Petrel" = "#3b9bb9", "Penguin" = "#3993b3", 
           "Noddy" = "#378bae", "Mollymawk" = "#3483a8", "Jaeger" = "#327ba3", 
           "Gull" = "#30739d", "Gannet" = "#2e6c98", "Fulmar" = "#2b6492", 
           "Frigatebird" = "#295c8d", "Cormorant" = "#275487", 
           "Booby" = "#244c82", "Albatross" = "#22447c")
)


# test inputs so they look like what shiny will give us for user inputs.
input <- list(
  sight_input = "Cormorant"
)

sight_map <-   birds_21 %>% 
    filter(bird_type %in% input$sight_input)
  
sight_map%>% 
    leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addAwesomeMarkers(~long, ~lat, icon=icons, 
                    label = sight_map$common_name,
                    clusterOptions = markerClusterOptions()) 
  
 # Print the map

getColor <- function(quakes) { sapply(position$date, function(date) { case_when(str_detect(date, regex(“^196”, ignore_case = TRUE)) ~ “green”, str_detect(date, regex(“^197”, ignore_case = TRUE)) ~ “orange”, str_detect(date, regex(“^198”, ignore_case = TRUE)) ~ “blue”, str_detect(date, regex(“^199”, ignore_case = TRUE)) ~ “red”

) }) }

icons <- awesomeIcons( icon = ‘ios-close’, iconColor = ‘black’, library = ‘ion’, markerColor = getColor(position) )

leaflet(position) %>% addTiles() %>% addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(date))

-45.91667 165.4000

Tab 4 Vessel Location tab

seabirds_cleaned_data
ship_data <- read_excel(here("raw_data/seabirds.xls"), 
                             sheet = "Ship data by record ID") %>% 
              clean_names()

position <- ship_data %>% 
  select(date, lat, long) %>%
  filter(!is.na(lat),
         !is.na(long)) %>% 
  group_by(date) %>% 
  summarise_if(is.numeric, mean)

position
ship_data %>% 
  select(date, lat, long) %>%
  filter(is.na(date))
tail(position)
leaflet(data = position) %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(label = position$date, clusterOptions = markerClusterOptions()) 
  
 # Print the map

https://kateto.net/network-visualization

https://stackoverflow.com/questions/38432788/how-do-i-visualise-multiple-routes-using-leaflet-in-r

# https://rstudio.github.io/leaflet/markers.html
# first 20 quakes
df.20 <- quakes[1:20,]

getColor <- function(quakes) {
  sapply(position$date, function(date) {
  if(date <= 1979-12-31) {
    "green"
  } else if(date <= 1989-12-31) {
    "orange"
  } else {
    "red"
  } })
}

icons <- awesomeIcons(
  icon = 'ios-close',
  iconColor = 'black',
  library = 'ion',
  markerColor = getColor(position)
)

leaflet(position) %>% addTiles() %>%
  addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(date))
getColor <- function(quakes) {
  sapply(position$date, function(date) {
  if(date %in% "^196") {
    "green"
  } else if(date  %in% "^197") {
    "orange"
  } else if(date  %in% "^198") {
    "blue"
  } else {
    "red"
  } })
}

icons <- awesomeIcons(
  icon = 'ios-close',
  iconColor = 'black',
  library = 'ion',
  markerColor = getColor(position)
)

leaflet(position) %>% addTiles() %>%
  addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(date))
getColor <- function(quakes) {
  sapply(position$date, function(date) {
  case_when(str_detect(date, 
             regex("^196", 
                   ignore_case = TRUE)) ~ "green",
            str_detect(date, 
             regex("^197", 
                   ignore_case = TRUE)) ~ "orange",
             str_detect(date, 
             regex("^198", 
                   ignore_case = TRUE)) ~ "blue",
             str_detect(date, 
             regex("^199", 
                   ignore_case = TRUE)) ~ "red"
    
  ) })
}

icons <- awesomeIcons(
  icon = 'ios-close',
  iconColor = 'black',
  library = 'ion',
  markerColor = getColor(position)
)

leaflet(position) %>% addTiles() %>%
  addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(date))
# https://stackoverflow.com/questions/56362519/how-to-filter-date-range-for-routes-in-r-leaflet-shiny-app

library(dplyr)
library(shiny)
library(leaflet)
library(readxl)
library(RColorBrewer)
library(maps)
Warning: package ‘maps’ was built under R version 4.1.3

Attaching package: ‘maps’

The following object is masked from ‘package:purrr’:

    map
library(leaflet.extras)
Warning: package ‘leaflet.extras’ was built under R version 4.1.3
library(htmlwidgets)
Warning: package ‘htmlwidgets’ was built under R version 4.1.2
data_dots = read_csv("test4.csv")
Rows: 8 Columns: 14
-- Column specification ---------------------------------------------------
Delimiter: ","
chr (6): Name, ship_date, delivery_date, ShipmentID, Dcity, Origin
dbl (8): Dzip, Dlong, Dlat, Route, Seq, Ozip, Olong, Olat

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
ui <- bootstrapPage(
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
  leafletOutput("map", width = "100%", height = "100%"),
  absolutePanel(top = 10, right = 10,

                dateRangeInput("dateRange", "Date Range Input", start =  min(data_dots$ship_date), end = max(data_dots$ship_date))


  )
)
Warning: Couldn't coerce the `end` argument to a date string with format yyyy-mm-dd
server <- function(input, output) {

  #n <- 60
  qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual', ]
  col_vector = unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals)))


  myMap = leaflet("map") %>% 
    addTiles(group = "Base") %>%
    addProviderTiles(providers$CartoDB.Positron, group = "Grey") %>%
    addResetMapButton()


  rv <- reactiveValues(
    filteredData =data_dots,
    ids = unique(data_dots$Route)
  )

  observeEvent(input$dateRange, 
               {rv$filteredData = data_dots[as.Date(data_dots$ship_date) >= input$dateRange[1] & as.Date(data_dots$ship_date) <= input$dateRange[2],]

               rv$ids = unique(rv$filteredData$Route)
               }

  )



  # Initiate the map
  output$map <- renderLeaflet({

    for (i in rv$ids) {
      #print(i)
      myMap = myMap %>%
        addPolylines(
          data = subset(rv$filteredData, Route == i),
          weight = 3,
          color = sample(col_vector, 1),
          opacity = 0.8,
          smoothFactor = 1,
          lng = ~Dlong, 
          lat = ~Dlat,
          highlight = highlightOptions(
            weight = 5,
            color = "blue",
            bringToFront = TRUE
          ),
          label = ~ as.character(ShipmentID),
          popup = ~ as.character(ShipmentID),
          group = "test"
        )

    }
    myMap


  })


}
shinyApp(ui = ui, server = server)

Listening on http://127.0.0.1:7348
Warning: Error in charToDate: character string is not in a standard unambiguous format
  [No stack trace available]
NA
data_dots %>% 
  mutate(ship_date = as.Date(ship_date, "%y/%m/%d"),
         delivery_date = as.Date(delivery_date, "%y/%m/%d"))
Error in mutate(., ship_date = as.Date(ship_date, "%y/%m/%d"), delivery_date = as.Date(delivery_date,  : 
  object 'data_dots' not found
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkoc2hpbnkpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2hpbnl0aGVtZXMpDQpsaWJyYXJ5KHN0cmluZ2kpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYA0KDQpgYGB7cn0NCnNlYWJpcmRzX2NsZWFuZWRfZGF0YSA8LSByZWFkX2NzdigiY2xlYW5fZGF0YS9zZWFiaXJkc19jbGVhbmVkX2RhdGEuY3N2IikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KYmlyZHNfMjEgPC0gc2VhYmlyZHNfY2xlYW5lZF9kYXRhICU+JQ0KICBtdXRhdGUoYmlyZF90eXBlID0gY2FzZV93aGVuKA0KICBzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiAgICAgICAgICAgICByZWdleCgic2hlYXJ3YXRlciIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIlNoZWFyd2F0ZXIiLA0KICBzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiAgICAgICAgICAgICByZWdleCgiYWxiYXRyb3NzIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiQWxiYXRyb3NzIiwNCiAgc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQogICAgICAgICAgICAgcmVnZXgoIm1vbGx5bWF3ayIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIk1vbGx5bWF3ayIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJwZXRyZWwiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJQZXRyZWwiLA0KICBzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiAgICAgICAgICAgICByZWdleCgicHJpb24iLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJQcmlvbiIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJza3VhIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiU2t1YSIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJwZW5ndWluIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiUGVuZ3VpbiIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJ0cm9waWNiaXJkIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiVHJvcGljYmlyZCIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJub2RkeSIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIk5vZGR5IiwNCiAgc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQogICAgICAgICAgICAgcmVnZXgoInRlcm4iLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJUZXJuIiwNCiAgc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQogICAgICAgICAgICAgcmVnZXgoImd1bGwiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJHdWxsIiwNCiAgc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQogICAgICAgICAgICAgcmVnZXgoImJvb2J5IiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiQm9vYnkiLA0KICBzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiAgICAgICAgICAgICByZWdleCgiZnJpZ2F0ZWJpcmQiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJGcmlnYXRlYmlyZCIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJzaGFnIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiU2hhZyIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJzaGVhdGhiaWxsIiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiU2hlYXRoYmlsbCIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJmdWxtYXIiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJGdWxtYXIiLA0KICBzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiAgICAgICAgICAgICByZWdleCgiZ2FubmV0IiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiR2FubmV0IiwNCiAgc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQogICAgICAgICAgICAgcmVnZXgoImNvcm1vcmFudCIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIkNvcm1vcmFudCIsDQogIHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJwcm9jZWxsYXJpYSIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIlByb2NlbGxhcmlhIiwNCiAgICBUUlVFIH4gY29tbW9uX25hbWUpKQ0KDQpiaXJkc18yMQ0KYGBgDQoNCmBgYHtyfQ0KYmlyZHNfMjEgJT4lIA0KICBhcnJhbmdlKGRhdGUpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIGh0dHBzOi8vci1jaGFydHMuY29tL2NvbG9yLXBhbGV0dGUtZ2VuZXJhdG9yLw0KDQojIGh0dHBzOi8vd3d3LnN0YXRvbG9neS5vcmcvY29sb3ItYnktZmFjdG9yLWdncGxvdDIvDQoNCmJpcmRzX3BhbCA8LSBjKCIjNTBlMmVhIiwgIiM0ZWRhZTUiLCAiIzRiZDJkZiIsICIjNDljYWRhIiwgIiM0N2MyZDQiLCANCiAgICAgICAgICAgICAgICIjNDViYmNmIiwgIiM0MmIzYzkiLCAiIzQwYWJjNCIsICIjM2VhM2JlIiwgIiMzYjliYjkiLA0KICAgICAgICAgICAgICAgIiMzOTkzYjMiLCAiIzM3OGJhZSIsICIjMzQ4M2E4IiwgIiMzMjdiYTMiLCAiIzMwNzM5ZCIsIA0KICAgICAgICAgICAgICAgIiMyZTZjOTgiLCAiIzJiNjQ5MiIsICIjMjk1YzhkIiwgIiMyNzU0ODciLCAiIzI0NGM4MiIsICIjMjI0NDdjIikNCg0KbmFtZXMoYmlyZHNfcGFsKSA8LSBsZXZlbHMoYmlyZHNfMjEkYmlyZF90eXBlKQ0KDQpjdXN0b21fY29sb3JzIDwtIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYmlyZHNfcGFsKQ0KICANCg0KYmlyZHMgPC0gYygiVHJvcGljYmlyZCIgPSAiIzUwZTJlYSIsICJUZXJuIiA9ICIjNGVkYWU1IiwgIlNrdWEiID0gIiM0YmQyZGYiLCANCiAgICAgICAgICAgIlNoZWF0aGJpbGwiID0gIiM0OWNhZGEiLCAiU2hlYXJ3YXRlciIgPSAiIzQ3YzJkNCIsIA0KICAgICAgICAgICAiU2hhZyIgPSAiIzQ1YmJjZiIsICJTZWFiaXJkIiA9ICIjNDJiM2M5IiwgIlByb2NlbGxhcmlhIiA9ICIjNDBhYmM0IiwNCiAgICAgICAgICAgIlByaW9uIiA9ICIjM2VhM2JlIiwgIlBldHJlbCIgPSAiIzNiOWJiOSIsICJQZW5ndWluIiA9ICIjMzk5M2IzIiwgDQogICAgICAgICAgICJOb2RkeSIgPSAiIzM3OGJhZSIsICJNb2xseW1hd2siID0gIiMzNDgzYTgiLCAiSmFlZ2VyIiA9ICIjMzI3YmEzIiwgDQogICAgICAgICAgICJHdWxsIiA9ICIjMzA3MzlkIiwgIkdhbm5ldCIgPSAiIzJlNmM5OCIsICJGdWxtYXIiID0gIiMyYjY0OTIiLCANCiAgICAgICAgICAgIkZyaWdhdGViaXJkIiA9ICIjMjk1YzhkIiwgIkNvcm1vcmFudCIgPSAiIzI3NTQ4NyIsIA0KICAgICAgICAgICAiQm9vYnkiID0gIiMyNDRjODIiLCAiQWxiYXRyb3NzIiA9ICIjMjI0NDdjIikNCg0KDQpgYGANCg0KSmFlZ2VyDQpTZWFiaXJkDQpgYGB7cn0NCmJpcmRzXzIxICU+JSANCiAgZmlsdGVyKCFpcy5uYShiaXJkX3R5cGUpKSAlPiUgDQogIGNvdW50KGJpcmRfdHlwZSkgIA0KICANCmBgYA0KDQojIEJpcmQgU2VlbiB0YWINCg0KIyMgbm90IHVwZGF0ZWQgYmlyZHMgc28gaGF2ZSAzIGV4dHJhIHdpdGggbm8gY29sb3VyIGFzaWduZWQsIHNoaW55IHVwIHRvIGRhdGUgDQoNCmBgYHtyfQ0Kc2lnaHRpbmcgPC0gIGJpcmRzXzIxICU+JSANCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlyZF90eXBlKSkgJT4lIA0KICAgICAgICAgICAgZ3JvdXBfYnkoYmlyZF90eXBlKSAlPiUgDQogICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBzdW0odG90YWxfc2lnaHRpbmcsIG5hLnJtID0gVFJVRSkpICU+JQ0KICAgICAgICAgICAgbXV0YXRlKHNpZ2h0aW5nX2lkID0gcm93X251bWJlcigpKQ0KDQpzaWdodGluZyAlPiUgDQogICAgZ2dwbG90KCkgKw0KICAgIGFlcyh5ID0gYmlyZF90eXBlLCANCiAgICAgICAgeCA9IGNvdW50LCBmaWxsID0gYmlyZF90eXBlKSArDQogICAgZ2VvbV9jb2woY29sb3VyID0gImJsYWNrIikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDEsIDUsIDEwLCAxMDAwLCA2MDAwLCAxNDAwMDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygxLDE0MDAwMDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSAibG9nMTAiKSArDQogICAgbGFicyh5ID0gIlxuIEJpcmQgTmFtZXMiLA0KICAgICAgICAgeCA9ICJOdW1iZXIgb2YgQmlyZHMgU2VlbiBcbiBMb2cxMCBzY2FsZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkcykNCg0KIyBsb2cxMCgpIGFzIDEgb3IgbW9yZSBiaXJkcyBhcmUgbGVzcyB0aGFuIDEwIGFuZCBkb24ndCBzaG93IG9uIG5vcm1hbCBncmFwaA0KYGBgDQoxLDM5NCw0NjgNCg0KYGBge3J9DQpmZWVkaW5nIDwtICBiaXJkc18yMSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KGJpcmRfdHlwZSkgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIoc3RyX2RldGVjdChmZWVkaW5nLCAiWUVTIikpICU+JSANCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShmZWVkaW5nX2lkID0gcm93X251bWJlcigpKQ0KDQpmZWVkaW5nICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgYWVzKHkgPSBiaXJkX3R5cGUsIA0KICAgICAgICB4ID0gY291bnQsIGZpbGwgPSBiaXJkX3R5cGUpICsNCiAgICBnZW9tX2NvbChjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMSwgNSwgMTAsIDEwMCwgMzAwLCA4MDApLA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDEsODAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHRyYW5zID0gImxvZzEwIikgKw0KICAgIGxhYnMoeSA9ICJcbiBCaXJkIE5hbWVzIiwNCiAgICAgICAgIHggPSAiTnVtYmVyIG9mIEJpcmRzIFNlZW4gRmVlZGluZyBcbiBMb2cxMCBzY2FsZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkcykNCiMgbG9nMTAoKSBhcyAxIG9yIG1vcmUgYmlyZHMgYXJlIGxlc3MgdGhhbiAxMCBhbmQgZG9uJ3Qgc2hvdyBvbiBub3JtYWwgZ3JhcGgNCmBgYA0KDQpgYGB7cn0NCm9uX3NoaXAgPC0gIGJpcmRzXzIxICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkoYmlyZF90eXBlKSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihzdHJfZGV0ZWN0KG9uX3NoaXAsICJZRVMiKSkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKG9uX3NoaXBfaWQgPSByb3dfbnVtYmVyKCkpDQoNCm9uX3NoaXAgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeSA9IGJpcmRfdHlwZSwgDQogICAgICAgIHggPSBjb3VudCwgZmlsbCA9IGJpcmRfdHlwZSkgKw0KICAgIGdlb21fY29sKGNvbG91ciA9ICJibGFjayIpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAyLCAzLCA1LCA3LCAxMCwgNjApLA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDEsNjApLCANCiAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSAibG9nMTAiKSArDQogICAgbGFicyh5ID0gIlxuIEJpcmQgTmFtZXMiLA0KICAgICAgICAgeCA9ICJOdW1iZXIgb2YgQmlyZHMgU2VlbiBPbiBTaGlwIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJpcmRzKQ0KYGBgDQoNCg0KYGBge3J9DQppbl9oYW5kIDwtICBiaXJkc18yMSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KGJpcmRfdHlwZSkgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIoc3RyX2RldGVjdChpbl9oYW5kLCAiWUVTIikpICU+JSANCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShpbl9oYW5kX2lkID0gcm93X251bWJlcigpKSANCg0KaW5faGFuZCAlPiUgDQogICAgZ2dwbG90KCkgKw0KICAgIGFlcyh5ID0gYmlyZF90eXBlLCANCiAgICAgICAgeCA9IGNvdW50LCBmaWxsID0gYmlyZF90eXBlKSArDQogICAgZ2VvbV9jb2woY29sb3VyID0gImJsYWNrIikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIGxhYnMoeSA9ICJcbiBCaXJkIE5hbWVzIiwNCiAgICAgICAgIHggPSAiTnVtYmVyIG9mIEJpcmRzIFNlZW4gSW4gSGFuZCIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkcykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNDI1NTUzMy9wcmV0dHktdGlja3MtZm9yLWxvZy1ub3JtYWwtc2NhbGUtdXNpbmctZ2dwbG90Mi1keW5hbWljLW5vdC1tYW51YWwNCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDM5NzQ4OTIvZHluYW1pYy1saW1pdHMtYW5kLWJyZWFrcy1pbi1zY2FsZS15LWNvbnRpbnVvdXMNCg0KDQpmbHlfYnkgPC0gIGJpcmRzXzIxICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkoYmlyZF90eXBlKSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihzdHJfZGV0ZWN0KGZseV9ieSwgIllFUyIpKSAlPiUgDQogICAgICAgICAgICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZmx5X2J5X2lkID0gcm93X251bWJlcigpKQ0KDQoNCiMgYmFzZV9icmVha3MgPC0gZnVuY3Rpb24obiA9IDEwKXsNCiMgICAgIGZ1bmN0aW9uKHgpIHsNCiMgICAgICAgICBheGlzVGlja3MobG9nMTAocmFuZ2UoZmx5X2J5JGNvdW50LCBuYS5ybSA9IFRSVUUpKSwgDQojICAgICAgICAgICAgICAgICAgIGxvZyA9IGlmX2Vsc2UobWF4KGZseV9ieSRjb3VudCkgPiAxMDAwLCBUUlVFLCBGQUxTRSksIG4gPSBuKQ0KIyAgICAgfQ0KIyB9DQoNCiMgYXNkIDwtIGlmX2Vsc2UobWF4KGZseV9ieSRjb3VudCkgPCAxMDAwLCBjKGxpbWl0cz1jKDAsbWF4KGZseV9ieSRjb3VudCkpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyAgPSBzZXEoMCxtYXgoZmx5X2J5JGNvdW50KSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IHJvdW5kKG1heChmbHlfYnkkY291bnQpLzUpKSksIA0KIyAgICAgICAgICAgICAgICBjKGxpbWl0cz1jKDAsbWF4KGZseV9ieSRjb3VudCkpLCANCiMgICAgICAgICAgICAgICAgICBicmVha3MgID0gc2VxKDAsbWF4KGZseV9ieSRjb3VudCksIA0KIyAgICAgICAgICAgICAgICAgICBieSA9IHJvdW5kKG1heChmbHlfYnkkY291bnQpLzUpKSwNCiMgICAgICAgICAgICAgICAgICB0cmFucyA9ICJsb2cxMCIpDQojICAgICAgICAgICAgICAgICkNCg0KZmx5X2J5ICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgYWVzKHkgPSBiaXJkX3R5cGUsIA0KICAgICAgICB4ID0gY291bnQsIGZpbGwgPSBiaXJkX3R5cGUpICsNCiAgICBnZW9tX2NvbChjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsbWF4KGZseV9ieSRjb3VudCkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzICA9IGMoc2VxKDAsbWF4KGZseV9ieSRjb3VudCksDQogICAgICAgICAgICAgICAgICAgICAgICBieSA9IChtYXgoZmx5X2J5JGNvdW50KS81KSkpLCB0cmFucyA9ICJsb2cxMCINCiAgICAgICAgICAgICAgICAgICAgICAgI3ZhbGlkYXRlKG1heChmbHlfYnkkY291bnQpIDwgMTAwMCwgdHJhbnMgPSAibG9nMTAiKQ0KICAgICAgICAgICAgICAgICAgICAgICApICsNCiAgICBsYWJzKHkgPSAiXG4gQmlyZCBOYW1lcyIsDQogICAgICAgICB4ID0gIk51bWJlciBvZiBCaXJkcyBTZWVuIEZseWluZyBCWVxuIExvZzEwIHNjYWxlIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJpcmRzKQ0KIyBsb2cxMCgpIGFzIDEgb3IgbW9yZSBiaXJkcyBhcmUgbGVzcyB0aGFuIDEwIGFuZCBkb24ndCBzaG93IG9uIG5vcm1hbCBncmFwaA0KYGBgDQpicmVha3MgPSBjKDEsIDUsIDEwLCAxMDAwLCA2MDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMSw2MDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHRyYW5zID0gImxvZzEwIg0KDQoNCiMgVGFiIDIgVmFyaWFudHMgdGFiDQoNCg0KYGBge3J9DQp2YXJpYW50cyA8LSBiaXJkc18yMSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihiaXJkX3R5cGUgPT0gIkFsYmF0cm9zcyIpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkoY29tbW9uX25hbWUpICU+JSANCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KICANCnZhcmlhbnRzICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgYWVzKHkgPSBjb21tb25fbmFtZSwgDQogICAgICAgIHggPSBjb3VudCwgZmlsbCA9IGNvbW1vbl9uYW1lKSArDQogICAgZ2VvbV9jb2woY29sb3VyID0gImJsYWNrIikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cygNCiAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSAibG9nMTAiKSArDQogICAgbGFicyh5ID0gIlxuIEJpcmQgTmFtZXMiLA0KICAgICAgICAgeCA9ICJOdW1iZXIgb2YgQmlyZHMgU2VlbiBGbHlpbmcgQllcbiBMb2cxMCBzY2FsZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkc19wYWwpICAgICAgICAgICAgDQpgYGANCg0KYGBge3J9DQp2YXJpYW50cyA8LSBiaXJkc18yMSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihiaXJkX3R5cGUgPT0gIkJvb2J5IikgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShjb21tb25fbmFtZSkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQogIA0KdmFyaWFudHMgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeSA9IGNvbW1vbl9uYW1lLCANCiAgICAgICAgeCA9IGNvdW50LCBmaWxsID0gY29tbW9uX25hbWUpICsNCiAgICBnZW9tX2NvbChjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKCANCiAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSAibG9nMTAiKSArDQogICAgbGFicyh5ID0gIlxuIEJpcmQgTmFtZXMiLA0KICAgICAgICAgeCA9ICJOdW1iZXIgb2YgQmlyZHMgU2VlbiBGbHlpbmcgQllcbiBMb2cxMCBzY2FsZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkc19wYWwpICANCmBgYA0KDQpgYGB7cn0NCnZhcmlhbnRzIDwtIGJpcmRzXzIxICU+JSANCiAgICAgICAgICAgICAgZmlsdGVyKGJpcmRfdHlwZSA9PSAiQ29ybW9yYW50IikgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShjb21tb25fbmFtZSkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQogIA0KdmFyaWFudHMgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeSA9IGNvbW1vbl9uYW1lLCANCiAgICAgICAgeCA9IGNvdW50LCBmaWxsID0gY29tbW9uX25hbWUpICsNCiAgICBnZW9tX2NvbChjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKCANCiAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSAibG9nMTAiKSArDQogICAgbGFicyh5ID0gIlxuIEJpcmQgTmFtZXMiLA0KICAgICAgICAgeCA9ICJOdW1iZXIgb2YgQmlyZHMgU2VlbiBGbHlpbmcgQllcbiBMb2cxMCBzY2FsZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaXJkc19wYWwpIA0KYGBgDQoNCmBgYHtyfQ0KdmFyaWFudHMgPC0gYmlyZHNfMjEgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIoYmlyZF90eXBlID09ICJUcm9waWNiaXJkIikgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShjb21tb25fbmFtZSkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQogIA0KdmFyaWFudHMgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeSA9IGNvbW1vbl9uYW1lLCANCiAgICAgICAgeCA9IGNvdW50LCBmaWxsID0gY29tbW9uX25hbWUpICsNCiAgICBnZW9tX2NvbChjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKCkgKw0KICAgIGxhYnMoeSA9ICJcbiBCaXJkIE5hbWVzIiwNCiAgICAgICAgIHggPSAiTnVtYmVyIG9mIEJpcmRzIFNlZW4gRmx5aW5nIEJZXG4gTG9nMTAgc2NhbGUiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYmlyZHNfcGFsKSANCmBgYA0KDQoNCg0KDQojIFRhYiAzIFNpZ2h0aW5ncyB0YWIgdGFiDQoNCg0KDQpgYGB7cn0NCmxpYnJhcnkoc2hpbnkpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2hpbnl0aGVtZXMpDQoNCg0KI3NlYWJpcmRzX2NsZWFuZWRfZGF0YSA8LSByZWFkX2NzdigiZGF0YS9zZWFiaXJkc19jbGVhbmVkX2RhdGEuY3N2IikNCmBgYA0KDQpgYGB7cn0NCiMgYmlyZHNfOSA8LSBzZWFiaXJkc19jbGVhbmVkX2RhdGEgJT4lIA0KIyAgIGdyb3VwX2J5KGNvbW1vbl9uYW1lKSAlPiUgDQojICAgbXV0YXRlKGNvbW1vbl9uYW1lID0gaWZfZWxzZShzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIig/aSlzaGVhcndhdGVyIiksIlNoZWFyd2F0ZXIiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9uYW1lKSwNCiMgICAgICAgICAgY29tbW9uX25hbWUgPSBpZl9lbHNlKHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD9pKWFsYmF0cm9zcyIpLCAiQWxiYXRyb3NzIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9uYW1lKSwNCiMgICAgICAgICAgY29tbW9uX25hbWUgPSBpZl9lbHNlKHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD9pKW1vbGx5bWF3ayIpLCAiTW9sbHltYXdrIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9uYW1lKSwNCiMgICAgICAgICAgY29tbW9uX25hbWUgPSBpZl9lbHNlKHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD9pKXBldHJlbCIpLCAiUGV0cmVsIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9uYW1lKSwNCiMgICAgICAgICAgY29tbW9uX25hbWUgPSBpZl9lbHNlKHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD9pKXByaW9uIiksICJQcmlvbiIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tb25fbmFtZSksDQojICAgICAgICAgIGNvbW1vbl9uYW1lID0gaWZfZWxzZShzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIig/aSlza3VhIiksICJTa3VhIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9uYW1lKSwNCiMgICAgICAgICAgY29tbW9uX25hbWUgPSBpZl9lbHNlKHN0cl9kZXRlY3QoY29tbW9uX25hbWUsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD9pKXBlbmd1aW4iKSwgIlBlbmd1aW4iLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbW9uX25hbWUpLA0KIyAgICAgICAgICBjb21tb25fbmFtZSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChjb21tb25fbmFtZSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIoP2kpUmVkLXRhaWxlZCB0cm9waWNiaXJkIiksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlZC10YWlsZWQgdHJvcGljYmlyZCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tb25fbmFtZSksDQojICAgICAgICAgIGNvbW1vbl9uYW1lID0gaWZfZWxzZShzdHJfZGV0ZWN0KGNvbW1vbl9uYW1lLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIig/aSlCcm93biBub2RkeSIpLCAiQnJvd24gbm9kZHkiLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbW9uX25hbWUpDQojICAgKSAlPiUgDQojICAgZmlsdGVyKGNvbW1vbl9uYW1lICVpbiUgYygiU2hlYXJ3YXRlciIsICJBbGJhdHJvc3MiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNb2xseW1hd2siLCAiUGV0cmVsIiwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJpb24iLCAiU2t1YSIsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBlbmd1aW4iLCAiQnJvd24gbm9kZHkiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWQtdGFpbGVkIHRyb3BpY2JpcmQiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBwYWwgPC0gYygiU2hlYXJ3YXRlciIgPSAiZ3JleSIsICJBbGJhdHJvc3MiID0gImJsdWUiLCANCiMgICAgICAgICAgIk1vbGx5bWF3ayIgPSAieWVsbG93IiwgIlBldHJlbCIgPSAiZ3JlZW4iLCANCiMgICAgICAgICAgIlByaW9uIiA9ICJwaW5rIiwgIlNrdWEiID0gInB1cnBsZSIsIA0KIyAgICAgICAgICAiUGVuZ3VpbiIgPSAib3JhbmdlIiwgIkJyb3duIG5vZGR5IiA9ICJicm93biIsIA0KIyAgICAgICAgICAiUmVkLXRhaWxlZCB0cm9waWNiaXJkIiA9ICJyZWQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBuYW1lcyhiaXJkc185KQ0KIyBoZWFkKGJpcmRzXzkpDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBiaXJkc185ICU+JSANCiMgICBncm91cF9ieShjb21tb25fbmFtZSkgJT4lIA0KIyAgIG11dGF0ZShmZWVkaW5nID0gaWZfZWxzZShmZWVkaW5nICVpbiUgIllFUyIsIDEsIDApLA0KIyAgICAgICAgICBvbl9zaGlwID0gaWZfZWxzZShvbl9zaGlwICVpbiUgIllFUyIsIDEsIDApLA0KIyAgICAgICAgICBpbl9oYW5kID0gaWZfZWxzZShpbl9oYW5kICVpbiUgIllFUyIsIDEsIDApLA0KIyAgICAgICAgICBmbHlfYnkgPSBpZl9lbHNlKGZseV9ieSAlaW4lICJZRVMiLCAxLCAwKSkgJT4lIA0KIyAgIHN1bW1hcmlzZShzaWdodGluZ19jb3VudCA9IHN1bSh0b3RhbF9zaWdodGluZywgbmEucm0gPSBUUlVFKSwNCiMgICAgICAgICAgICAgZmVlZGluZ19jb3VudCA9IHN1bShmZWVkaW5nLCBuYS5ybSA9IFRSVUUpLA0KIyAgICAgICAgICAgICBvbl9zaGlwX2NvdW50ID0gc3VtKG9uX3NoaXAsIG5hLnJtID0gVFJVRSksDQojICAgICAgICAgICAgIGluX2hhbmRfY291bnQgPSBzdW0oaW5faGFuZCwgbmEucm0gPSBUUlVFKSwNCiMgICAgICAgICAgICAgZmx5X2J5X2NvdW50ID0gc3VtKGZseV9ieSwgbmEucm0gPSBUUlVFKSkgDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkobGVhZmxldCkNCmBgYA0KDQpgYGB7cn0NCg0KIyB0ZXN0IGlucHV0cyBzbyB0aGV5IGxvb2sgbGlrZSB3aGF0IHNoaW55IHdpbGwgZ2l2ZSB1cyBmb3IgdXNlciBpbnB1dHMuDQppbnB1dCA8LSBsaXN0KA0KICBzaWdodF9pbnB1dCA9ICJDb3Jtb3JhbnQiDQopDQoNCnNpZ2h0X21hcCA8LSAgIGJpcmRzXzIxICU+JSANCiAgICBmaWx0ZXIoYmlyZF90eXBlICVpbiUgaW5wdXQkc2lnaHRfaW5wdXQpDQogIA0Kc2lnaHRfbWFwJT4lIA0KICAgIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUgICMgQWRkIGRlZmF1bHQgT3BlblN0cmVldE1hcCBtYXAgdGlsZXMNCiAgYWRkTWFya2VycyhsYWJlbCA9IHNpZ2h0X21hcCRjb21tb25fbmFtZSwgY2x1c3Rlck9wdGlvbnMgPSANCiAgICAgICAgICAgICAgIG1hcmtlckNsdXN0ZXJPcHRpb25zKCkpIA0KICANCiAjIFByaW50IHRoZSBtYXANCmBgYA0KDQoNCmBgYHtyfQ0KZ2V0Q29sb3IgPC0gZnVuY3Rpb24oc2lnaHRfbWFwKSB7DQogIHNhcHBseShzaWdodF9tYXAkYmlyZF90eXBlLCBmdW5jdGlvbihzaWdodF9tYXApIHsNCiAgY2FzZV93aGVuKHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIlRyb3BpY2JpcmQiIH4gIiM1MGUyZWEiLA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiVGVybiIgfiAiIzRlZGFlNSIsDQogICAgICAgICAgICBzaWdodF9tYXAkYmlyZF90eXBlID09ICJTa3VhIiB+ICIjNGJkMmRmIiwNCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIlNoZWF0aGJpbGwiIH4gIiM0OWNhZGEiLA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiU2hlYXJ3YXRlciIgfiAiIzQ3YzJkNCIsDQogICAgICAgICAgICBzaWdodF9tYXAkYmlyZF90eXBlID09ICJTaGFnIiB+ICIjNDViYmNmIiwNCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIlNlYWJpcmQiIH4gIiM0MmIzYzkiLA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiUHJvY2VsbGFyaWEiIH4gIiM0MGFiYzQiLA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiUHJpb24iIH4gIiMzZWEzYmUiLCANCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIlBldHJlbCIgfiAiIzNiOWJiOSIsIA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiUGVuZ3VpbiIgfiAiIzM5OTNiMyIsDQogICAgICAgICAgICBzaWdodF9tYXAkYmlyZF90eXBlID09ICJOb2RkeSIgfiAiIzM3OGJhZSIsIA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiTW9sbHltYXdrIiB+ICIjMzQ4M2E4IiwgDQogICAgICAgICAgICBzaWdodF9tYXAkYmlyZF90eXBlID09ICJKYWVnZXIiIH4gIiMzMjdiYTMiLCANCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIkd1bGwiIH4gIiMzMDczOWQiLCANCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIkdhbm5ldCIgfiAiIzJlNmM5OCIsIA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiRnVsbWFyIiB+ICIjMmI2NDkyIiwNCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIkZyaWdhdGViaXJkIiB+ICIjMjk1YzhkIiwNCiAgICAgICAgICAgIHNpZ2h0X21hcCRiaXJkX3R5cGUgPT0gIkNvcm1vcmFudCIgfiAiIzI3NTQ4NyIsDQogICAgICAgICAgICBzaWdodF9tYXAkYmlyZF90eXBlID09ICJCb29ieSIgfiAiIzI0NGM4MiIsIA0KICAgICAgICAgICAgc2lnaHRfbWFwJGJpcmRfdHlwZSA9PSAiQWxiYXRyb3NzIiB+ICIjMjI0NDdjIiwNCiAgICAgICAgICAgIFRSVUUgfiBzaWdodF9tYXAkYmlyZF90eXBlDQogICAgDQogICkgfSkNCn0NCg0KDQppY29ucyA8LSBhd2Vzb21lSWNvbnMoDQogIGljb24gPSAnaW9zLWNsb3NlJywNCiAgaWNvbkNvbG9yID0gJ2JsYWNrJywNCiAgbGlicmFyeSA9ICdpb24nLA0KICBtYXJrZXJDb2xvciA9IGxpc3QoIlRyb3BpY2JpcmQiID0gIiM1MGUyZWEiLCAiVGVybiIgPSAiIzRlZGFlNSIsICJTa3VhIiA9ICIjNGJkMmRmIiwgDQogICAgICAgICAgICJTaGVhdGhiaWxsIiA9ICIjNDljYWRhIiwgIlNoZWFyd2F0ZXIiID0gIiM0N2MyZDQiLCANCiAgICAgICAgICAgIlNoYWciID0gIiM0NWJiY2YiLCAiU2VhYmlyZCIgPSAiIzQyYjNjOSIsICJQcm9jZWxsYXJpYSIgPSAiIzQwYWJjNCIsDQogICAgICAgICAgICJQcmlvbiIgPSAiIzNlYTNiZSIsICJQZXRyZWwiID0gIiMzYjliYjkiLCAiUGVuZ3VpbiIgPSAiIzM5OTNiMyIsIA0KICAgICAgICAgICAiTm9kZHkiID0gIiMzNzhiYWUiLCAiTW9sbHltYXdrIiA9ICIjMzQ4M2E4IiwgIkphZWdlciIgPSAiIzMyN2JhMyIsIA0KICAgICAgICAgICAiR3VsbCIgPSAiIzMwNzM5ZCIsICJHYW5uZXQiID0gIiMyZTZjOTgiLCAiRnVsbWFyIiA9ICIjMmI2NDkyIiwgDQogICAgICAgICAgICJGcmlnYXRlYmlyZCIgPSAiIzI5NWM4ZCIsICJDb3Jtb3JhbnQiID0gIiMyNzU0ODciLCANCiAgICAgICAgICAgIkJvb2J5IiA9ICIjMjQ0YzgyIiwgIkFsYmF0cm9zcyIgPSAiIzIyNDQ3YyIpDQopDQoNCg0KIyB0ZXN0IGlucHV0cyBzbyB0aGV5IGxvb2sgbGlrZSB3aGF0IHNoaW55IHdpbGwgZ2l2ZSB1cyBmb3IgdXNlciBpbnB1dHMuDQppbnB1dCA8LSBsaXN0KA0KICBzaWdodF9pbnB1dCA9ICJDb3Jtb3JhbnQiDQopDQoNCnNpZ2h0X21hcCA8LSAgIGJpcmRzXzIxICU+JSANCiAgICBmaWx0ZXIoYmlyZF90eXBlICVpbiUgaW5wdXQkc2lnaHRfaW5wdXQpDQogIA0Kc2lnaHRfbWFwJT4lIA0KICAgIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUgICMgQWRkIGRlZmF1bHQgT3BlblN0cmVldE1hcCBtYXAgdGlsZXMNCiAgYWRkQXdlc29tZU1hcmtlcnMofmxvbmcsIH5sYXQsIGljb249aWNvbnMsIA0KICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNpZ2h0X21hcCRjb21tb25fbmFtZSwNCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucygpKSANCiAgDQogIyBQcmludCB0aGUgbWFwDQpgYGANCg0KZ2V0Q29sb3IgPC0gZnVuY3Rpb24ocXVha2VzKSB7DQogIHNhcHBseShwb3NpdGlvbiRkYXRlLCBmdW5jdGlvbihkYXRlKSB7DQogIGNhc2Vfd2hlbihzdHJfZGV0ZWN0KGRhdGUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJeMTk2IiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAiZ3JlZW4iLA0KICAgICAgICAgICAgc3RyX2RldGVjdChkYXRlLCANCiAgICAgICAgICAgICByZWdleCgiXjE5NyIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gIm9yYW5nZSIsDQogICAgICAgICAgICAgc3RyX2RldGVjdChkYXRlLCANCiAgICAgICAgICAgICByZWdleCgiXjE5OCIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gImJsdWUiLA0KICAgICAgICAgICAgIHN0cl9kZXRlY3QoZGF0ZSwgDQogICAgICAgICAgICAgcmVnZXgoIl4xOTkiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJyZWQiDQogICAgDQogICkgfSkNCn0NCg0KDQppY29ucyA8LSBhd2Vzb21lSWNvbnMoDQogIGljb24gPSAnaW9zLWNsb3NlJywNCiAgaWNvbkNvbG9yID0gJ2JsYWNrJywNCiAgbGlicmFyeSA9ICdpb24nLA0KICBtYXJrZXJDb2xvciA9IGdldENvbG9yKHBvc2l0aW9uKQ0KKQ0KDQpsZWFmbGV0KHBvc2l0aW9uKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQXdlc29tZU1hcmtlcnMofmxvbmcsIH5sYXQsIGljb249aWNvbnMsIGxhYmVsPX5hcy5jaGFyYWN0ZXIoZGF0ZSkpDQoNCg0KDQoNCi00NS45MTY2Nw0KMTY1LjQwMDANCg0KIyBUYWIgNCBWZXNzZWwgTG9jYXRpb24gdGFiDQoNCg0KYGBge3J9DQpzZWFiaXJkc19jbGVhbmVkX2RhdGENCmBgYA0KDQpgYGB7cn0NCnNoaXBfZGF0YSA8LSByZWFkX2V4Y2VsKGhlcmUoInJhd19kYXRhL3NlYWJpcmRzLnhscyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiU2hpcCBkYXRhIGJ5IHJlY29yZCBJRCIpICU+JSANCiAgICAgICAgICAgICAgY2xlYW5fbmFtZXMoKQ0KDQpwb3NpdGlvbiA8LSBzaGlwX2RhdGEgJT4lIA0KICBzZWxlY3QoZGF0ZSwgbGF0LCBsb25nKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsYXQpLA0KICAgICAgICAgIWlzLm5hKGxvbmcpKSAlPiUgDQogIGdyb3VwX2J5KGRhdGUpICU+JSANCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCnBvc2l0aW9uDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc2hpcF9kYXRhICU+JSANCiAgc2VsZWN0KGRhdGUsIGxhdCwgbG9uZykgJT4lDQogIGZpbHRlcihpcy5uYShkYXRlKSkNCmBgYA0KDQoNCg0KYGBge3J9DQp0YWlsKHBvc2l0aW9uKQ0KYGBgDQoNCg0KYGBge3J9DQpsZWFmbGV0KGRhdGEgPSBwb3NpdGlvbikgJT4lDQogIGFkZFRpbGVzKCkgJT4lICAjIEFkZCBkZWZhdWx0IE9wZW5TdHJlZXRNYXAgbWFwIHRpbGVzDQogIGFkZE1hcmtlcnMobGFiZWwgPSBwb3NpdGlvbiRkYXRlLCBjbHVzdGVyT3B0aW9ucyA9IG1hcmtlckNsdXN0ZXJPcHRpb25zKCkpIA0KICANCiAjIFByaW50IHRoZSBtYXANCmBgYA0KDQojIGh0dHBzOi8va2F0ZXRvLm5ldC9uZXR3b3JrLXZpc3VhbGl6YXRpb24NCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzg0MzI3ODgvaG93LWRvLWktdmlzdWFsaXNlLW11bHRpcGxlLXJvdXRlcy11c2luZy1sZWFmbGV0LWluLXINCg0KDQpgYGB7cn0NCiMgaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0L21hcmtlcnMuaHRtbA0KIyBmaXJzdCAyMCBxdWFrZXMNCmRmLjIwIDwtIHF1YWtlc1sxOjIwLF0NCg0KZ2V0Q29sb3IgPC0gZnVuY3Rpb24ocXVha2VzKSB7DQogIHNhcHBseShwb3NpdGlvbiRkYXRlLCBmdW5jdGlvbihkYXRlKSB7DQogIGlmKGRhdGUgPD0gMTk3OS0xMi0zMSkgew0KICAgICJncmVlbiINCiAgfSBlbHNlIGlmKGRhdGUgPD0gMTk4OS0xMi0zMSkgew0KICAgICJvcmFuZ2UiDQogIH0gZWxzZSB7DQogICAgInJlZCINCiAgfSB9KQ0KfQ0KDQppY29ucyA8LSBhd2Vzb21lSWNvbnMoDQogIGljb24gPSAnaW9zLWNsb3NlJywNCiAgaWNvbkNvbG9yID0gJ2JsYWNrJywNCiAgbGlicmFyeSA9ICdpb24nLA0KICBtYXJrZXJDb2xvciA9IGdldENvbG9yKHBvc2l0aW9uKQ0KKQ0KDQpsZWFmbGV0KHBvc2l0aW9uKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQXdlc29tZU1hcmtlcnMofmxvbmcsIH5sYXQsIGljb249aWNvbnMsIGxhYmVsPX5hcy5jaGFyYWN0ZXIoZGF0ZSkpDQpgYGANCg0KYGBge3J9DQpnZXRDb2xvciA8LSBmdW5jdGlvbihxdWFrZXMpIHsNCiAgc2FwcGx5KHBvc2l0aW9uJGRhdGUsIGZ1bmN0aW9uKGRhdGUpIHsNCiAgaWYoZGF0ZSAlaW4lICJeMTk2Iikgew0KICAgICJncmVlbiINCiAgfSBlbHNlIGlmKGRhdGUgICVpbiUgIl4xOTciKSB7DQogICAgIm9yYW5nZSINCiAgfSBlbHNlIGlmKGRhdGUgICVpbiUgIl4xOTgiKSB7DQogICAgImJsdWUiDQogIH0gZWxzZSB7DQogICAgInJlZCINCiAgfSB9KQ0KfQ0KDQppY29ucyA8LSBhd2Vzb21lSWNvbnMoDQogIGljb24gPSAnaW9zLWNsb3NlJywNCiAgaWNvbkNvbG9yID0gJ2JsYWNrJywNCiAgbGlicmFyeSA9ICdpb24nLA0KICBtYXJrZXJDb2xvciA9IGdldENvbG9yKHBvc2l0aW9uKQ0KKQ0KDQpsZWFmbGV0KHBvc2l0aW9uKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQXdlc29tZU1hcmtlcnMofmxvbmcsIH5sYXQsIGljb249aWNvbnMsIGxhYmVsPX5hcy5jaGFyYWN0ZXIoZGF0ZSkpDQpgYGANCg0KDQpgYGB7cn0NCmdldENvbG9yIDwtIGZ1bmN0aW9uKHF1YWtlcykgew0KICBzYXBwbHkocG9zaXRpb24kZGF0ZSwgZnVuY3Rpb24oZGF0ZSkgew0KICBjYXNlX3doZW4oc3RyX2RldGVjdChkYXRlLCANCiAgICAgICAgICAgICByZWdleCgiXjE5NiIsIA0KICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpIH4gImdyZWVuIiwNCiAgICAgICAgICAgIHN0cl9kZXRlY3QoZGF0ZSwgDQogICAgICAgICAgICAgcmVnZXgoIl4xOTciLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJvcmFuZ2UiLA0KICAgICAgICAgICAgIHN0cl9kZXRlY3QoZGF0ZSwgDQogICAgICAgICAgICAgcmVnZXgoIl4xOTgiLCANCiAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSB+ICJibHVlIiwNCiAgICAgICAgICAgICBzdHJfZGV0ZWN0KGRhdGUsIA0KICAgICAgICAgICAgIHJlZ2V4KCJeMTk5IiwgDQogICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkgfiAicmVkIg0KICAgIA0KICApIH0pDQp9DQoNCmljb25zIDwtIGF3ZXNvbWVJY29ucygNCiAgaWNvbiA9ICdpb3MtY2xvc2UnLA0KICBpY29uQ29sb3IgPSAnYmxhY2snLA0KICBsaWJyYXJ5ID0gJ2lvbicsDQogIG1hcmtlckNvbG9yID0gZ2V0Q29sb3IocG9zaXRpb24pDQopDQoNCmxlYWZsZXQocG9zaXRpb24pICU+JSBhZGRUaWxlcygpICU+JQ0KICBhZGRBd2Vzb21lTWFya2Vycyh+bG9uZywgfmxhdCwgaWNvbj1pY29ucywgbGFiZWw9fmFzLmNoYXJhY3RlcihkYXRlKSkNCmBgYA0KDQoNCg0KDQoNCg0KYGBge3J9DQojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU2MzYyNTE5L2hvdy10by1maWx0ZXItZGF0ZS1yYW5nZS1mb3Itcm91dGVzLWluLXItbGVhZmxldC1zaGlueS1hcHANCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc2hpbnkpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShtYXBzKQ0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQoNCg0KDQpkYXRhX2RvdHMgPSByZWFkX2NzdigidGVzdDQuY3N2IikNCg0KDQp1aSA8LSBib290c3RyYXBQYWdlKA0KICB0YWdzJHN0eWxlKHR5cGUgPSAidGV4dC9jc3MiLCAiaHRtbCwgYm9keSB7d2lkdGg6MTAwJTtoZWlnaHQ6MTAwJX0iKSwNCiAgbGVhZmxldE91dHB1dCgibWFwIiwgd2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIxMDAlIiksDQogIGFic29sdXRlUGFuZWwodG9wID0gMTAsIHJpZ2h0ID0gMTAsDQoNCg0KICAgICAgICAgICAgICAgIGRhdGVSYW5nZUlucHV0KCJkYXRlUmFuZ2UiLCAiRGF0ZSBSYW5nZSBJbnB1dCIsIHN0YXJ0ID0gIG1pbihkYXRhX2RvdHMkc2hpcF9kYXRlKSwgZW5kID0gbWF4KGRhdGFfZG90cyRzaGlwX2RhdGUpKQ0KDQoNCiAgKQ0KKQ0KDQoNCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQoNCiAgI24gPC0gNjANCiAgcXVhbF9jb2xfcGFscyA9IGJyZXdlci5wYWwuaW5mb1ticmV3ZXIucGFsLmluZm8kY2F0ZWdvcnkgPT0gJ3F1YWwnLCBdDQogIGNvbF92ZWN0b3IgPSB1bmxpc3QobWFwcGx5KGJyZXdlci5wYWwsIHF1YWxfY29sX3BhbHMkbWF4Y29sb3JzLCByb3duYW1lcyhxdWFsX2NvbF9wYWxzKSkpDQoNCg0KICBteU1hcCA9IGxlYWZsZXQoIm1hcCIpICU+JSANCiAgICBhZGRUaWxlcyhncm91cCA9ICJCYXNlIikgJT4lDQogICAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbiwgZ3JvdXAgPSAiR3JleSIpICU+JQ0KICAgIGFkZFJlc2V0TWFwQnV0dG9uKCkNCg0KDQogIHJ2IDwtIHJlYWN0aXZlVmFsdWVzKA0KICAgIGZpbHRlcmVkRGF0YSA9ZGF0YV9kb3RzLA0KICAgIGlkcyA9IHVuaXF1ZShkYXRhX2RvdHMkUm91dGUpDQogICkNCg0KICBvYnNlcnZlRXZlbnQoaW5wdXQkZGF0ZVJhbmdlLCANCiAgICAgICAgICAgICAgIHtydiRmaWx0ZXJlZERhdGEgPSBkYXRhX2RvdHNbYXMuRGF0ZShkYXRhX2RvdHMkc2hpcF9kYXRlKSA+PSBpbnB1dCRkYXRlUmFuZ2VbMV0gJiBhcy5EYXRlKGRhdGFfZG90cyRzaGlwX2RhdGUpIDw9IGlucHV0JGRhdGVSYW5nZVsyXSxdDQoNCiAgICAgICAgICAgICAgIHJ2JGlkcyA9IHVuaXF1ZShydiRmaWx0ZXJlZERhdGEkUm91dGUpDQogICAgICAgICAgICAgICB9DQoNCiAgKQ0KDQoNCg0KICAjIEluaXRpYXRlIHRoZSBtYXANCiAgb3V0cHV0JG1hcCA8LSByZW5kZXJMZWFmbGV0KHsNCg0KDQogICAgZm9yIChpIGluIHJ2JGlkcykgew0KICAgICAgI3ByaW50KGkpDQogICAgICBteU1hcCA9IG15TWFwICU+JQ0KICAgICAgICBhZGRQb2x5bGluZXMoDQogICAgICAgICAgZGF0YSA9IHN1YnNldChydiRmaWx0ZXJlZERhdGEsIFJvdXRlID09IGkpLA0KICAgICAgICAgIHdlaWdodCA9IDMsDQogICAgICAgICAgY29sb3IgPSBzYW1wbGUoY29sX3ZlY3RvciwgMSksDQogICAgICAgICAgb3BhY2l0eSA9IDAuOCwNCiAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAxLA0KICAgICAgICAgIGxuZyA9IH5EbG9uZywgDQogICAgICAgICAgbGF0ID0gfkRsYXQsDQogICAgICAgICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucygNCiAgICAgICAgICAgIHdlaWdodCA9IDUsDQogICAgICAgICAgICBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICAgIGJyaW5nVG9Gcm9udCA9IFRSVUUNCiAgICAgICAgICApLA0KICAgICAgICAgIGxhYmVsID0gfiBhcy5jaGFyYWN0ZXIoU2hpcG1lbnRJRCksDQogICAgICAgICAgcG9wdXAgPSB+IGFzLmNoYXJhY3RlcihTaGlwbWVudElEKSwNCiAgICAgICAgICBncm91cCA9ICJ0ZXN0Ig0KICAgICAgICApDQoNCg0KICAgIH0NCiAgICBteU1hcA0KDQoNCiAgfSkNCg0KDQp9DQpzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpDQpgYGANCg0KYGBge3J9DQpkYXRhX2RvdHMgJT4lIA0KICBtdXRhdGUoc2hpcF9kYXRlID0gYXMuRGF0ZShzaGlwX2RhdGUsICIleS8lbS8lZCIpLA0KICAgICAgICAgZGVsaXZlcnlfZGF0ZSA9IGFzLkRhdGUoZGVsaXZlcnlfZGF0ZSwgIiV5LyVtLyVkIikpDQpgYGANCg==